/*
	snowman.cpp

	The CSnowman class implementation

	OpenGL Game Programming
	Author: Kevin Hawkins
	Date: 3/30/2001
	Description:

*/

#include "snowman.h"

#include "terrain.h"
#include "player.h"
#include "cloud.h"
#include "snowball.h"
#include "lightbeamdamager.h"
#include "hat.h"


void CSnowman::OnCollision(CObject *collisionObject)
{
		// as long as the model is not dying, it can collide with other objects
	if (modelState != MODEL_DIE)
	{
		// if this enemy collides with another enemy
		if ((typeid(*collisionObject) == typeid(CSnowman)))
		{
			aiState = AI_UNCARING;
		}

		// if this enemy collides with the terrain (always)
		else	if (typeid(*collisionObject) == typeid(CTerrain))
		{
			float halfScale = scale/2.0f;
			float drawScale = halfScale + (halfScale * (hitPoints / 1000.0f));
			position.y = ((CTerrain*)collisionObject)->GetHeight(position.x, position.z) + drawScale + drawScale*size*0.5f;
			if (position.x <= ((CTerrain*)collisionObject)->GetScanDepth())
				position.x = ((CTerrain*)collisionObject)->GetScanDepth();
			if (position.x >= ((CTerrain*)collisionObject)->GetWidth()*((CTerrain*)collisionObject)->GetMul() - ((CTerrain*)collisionObject)->GetScanDepth())
				position.x = ((CTerrain*)collisionObject)->GetWidth()*((CTerrain*)collisionObject)->GetMul() - ((CTerrain*)collisionObject)->GetScanDepth();
			if (position.z <= ((CTerrain*)collisionObject)->GetScanDepth())
				position.z = ((CTerrain*)collisionObject)->GetScanDepth();
			if (position.z >= ((CTerrain*)collisionObject)->GetWidth()*((CTerrain*)collisionObject)->GetMul() - ((CTerrain*)collisionObject)->GetScanDepth())
				position.z = ((CTerrain*)collisionObject)->GetWidth()*((CTerrain*)collisionObject)->GetMul() - ((CTerrain*)collisionObject)->GetScanDepth();
		}
		else if (typeid(*collisionObject) == typeid(CSnowBall))
		{
			if(!((CSnowBall*)collisionObject)->IsUsedUp())
			{
				((CSnowBall*)collisionObject)->SetUsedUp(true);
				hitPoints += 100;
			}
		}
		else if (typeid(*collisionObject) == typeid(CLightBeamDamager))
		{
			if(!((CLightBeamDamager*)collisionObject)->IsUsedUp())
			{
				((CLightBeamDamager*)collisionObject)->SetUsedUp(true);
				hitPoints -= 25;
			}
		}
	}
}

// Fixed the calculation of dirToPlayer
// Added/changed behaviors for AI states
// Jason - 15 Nov 2012
void CSnowman::OnPrepare() 
{
	float dirToPlayer;	// the angle of the enemy-player vector

	CVector diff;		// the vector from the enemy to the player
	diff.x = position.x - player->position.x;
	diff.z = position.z - player->position.z;
	diff.Normalize();
	// find the angle in the world of the vector from the enemy to the cloud
	// in relation the negative x-axis
	dirToPlayer = RAD2DEG(diff.Angle(CVector(1,0,0)));
	dirToPlayer = (diff.z >= 0) ? dirToPlayer + 180 : -dirToPlayer + 180;

	// Cloud variables for calculations
	CTerrain* terrain = (CTerrain*)this->parentNode;
	CCloud* closestCloud = (CCloud*)terrain->GetClosestCloud(this);
	// Get the cloud's positiion
	diff.x = position.x - closestCloud->position.x;
	diff.z = position.z - closestCloud->position.z;
	diff.Normalize();
	// find the angle in the world of the vector from the enemy to the player
	// in relation the negative x-axis
	float dirToCloud  = RAD2DEG(diff.Angle(CVector(1,0,0)));
	dirToCloud = (diff.z >= 0) ? dirToCloud + 180 : -dirToCloud + 180;

	// seed random generator
	srand((unsigned int)time(NULL));

	ProcessAI();
	// now do Snowman prep
	// set modelState based on AIstate
	// if AI_SCARED, then MODEL_RUN
	switch (aiState)
	{
	case AI_SCARED:
		direction = (dirToPlayer - 180) + ((rand()%30)-15);	// set the direction of the enemy
		modelState = MODEL_RUN;
		velocity = CVector(0.0, 0.0, 15.0);
		break;
	case AI_AGGRESSIVE:
		direction = dirToPlayer;
		modelState = MODEL_RUN;
		velocity = CVector(0.0, 0.0, 15.0);
		break;
	case AI_ALERT:
		hitPoints += 0.2f;
		direction = dirToPlayer;
		modelState = MODEL_IDLE;
		velocity = CVector(0.0, 0.0, 0.0);
		break;
	case AI_ATTACKING:
		direction = dirToPlayer;
		modelState = MODEL_JUMP;
		velocity = CVector(0.0, 0.0, 70.0);
		if (distFromPlayer < 10.0)
		{
			velocity = CVector(0.0, 0.0, 0.0);
			canChangeAction = true;
		}
		break;
	case AI_JUMPBACK:
		direction = dirToPlayer;
		modelState = MODEL_JUMP;
		canChangeAction = false;
		velocity = CVector(0.0, 0.0, -70.0);
		if (nextFrame >= stateEnd - 3)
		{
			velocity = CVector(0.0, 0.0, 0.0);
			canChangeAction = true;
		}
		break;
	case AI_MELTING:
		hitPoints -= 1;
		direction = dirToCloud;
		modelState = MODEL_RUN;
		velocity = CVector(0.0, 0.0, 30.0);
		break;
	case AI_UNCARING:
		hitPoints += 0.2f;
		if (hitPoints >= 1000) { hitPoints = 1000; }
		if ((rand() % 3) == 0)
		{
			direction = float(direction + (rand() % 10));
			modelState = MODEL_RUN;
			velocity = CVector(0.0, 0.0, 10.0);
		}
		break;
	case AI_DEAD:
		modelState = MODEL_DIE;
		velocity = CVector(0.0, 0.0, 0.0);
		isDead = true;
		break;
	default:
		break;
	}

	// do prep for MD2 Model states
	CEntity::OnPrepare();
}

void CSnowman::Load()
{
	// load model
	//CMD2Model::Load("..\\..\\res\\models\\sodf8\\tris.md2", "..\\..\\res\\models\\sodf8\\abarlith.pcx");
	CMD2Model::Load("..\\..\\res\\models\\snowman\\snowman.md2", "..\\..\\res\\models\\snowman\\snowman.pcx");
}

// Changed the rules for AI state changing
// Jason - 15 Nov 2012
void CSnowman::OnProcessAI()
{
	// calculate distance from player
	CVector diff = player->position - position;

	if (aiState != AI_DEAD)
	{
		srand((unsigned int) clock());

		// Get distance to closest cloud
		CTerrain* terrain = (CTerrain*)this->parentNode;
		CCloud* closestCloud = (CCloud*)terrain->GetClosestCloud(this);
		float dx = this->position.x - closestCloud->position.x;
		float dz = this->position.z - closestCloud->position.z;
		float distToCloud = sqrt(dx*dx + dz*dz);
		
		// Get distance to player
		distFromPlayer = sqrt(diff.x*diff.x + diff.y*diff.y + diff.z*diff.z);

		if (canChangeAction)
		{
			// Calculate how badly we want to stay alive
			float desireToLive = 0.0;
			AIState_t stayAliveAi = AI_MELTING;
			if (distToCloud < 40.0)				// We are close enough to the cloud to not melt
			{
				stayAliveAi = AI_UNCARING;
				canChangeAction = true;
				desireToLive = 0.1f;
			}
			else if (distToCloud > 100.0)		// We are far away from the cloud and need to run to safety
			{
				stayAliveAi = AI_MELTING;
				canChangeAction = true;
				desireToLive = distToCloud / 200.0f;
			}

			// Calculate how badly we want to  kill the player
			float desireToKill = 0.0;
			AIState_t killPlayerAi = aiState;
			if (distFromPlayer < 15.0)			// We're too close to the player, jump away
			{
				killPlayerAi = AI_JUMPBACK;
				canChangeAction = false;
				desireToKill = 10.0;
			}
			else if (distFromPlayer < 35.0)		// We're close enough to attack
			{
				killPlayerAi = AI_ATTACKING;
				canChangeAction = false;
				desireToKill = 10.0;
			}
			else if (distFromPlayer < 100.0)		// Be aggressive if we're towards the player
			{
				killPlayerAi = AI_AGGRESSIVE;
				canChangeAction = true;
				desireToKill = 2.0f - (distFromPlayer / 125.0f);
			}
			else								// We're not close enough to do anything, so we will watch the player
			{
				killPlayerAi = AI_ALERT;
				canChangeAction = true;
				desireToKill = 1.0f - (distFromPlayer / 250.0f);
			}

			// Calculate which ai state we want to have
			aiState = (desireToKill > desireToLive) ? killPlayerAi : stayAliveAi;
		}

		if (hitPoints <= 0)
		{
			// kill the sod
			aiState = AI_DEAD;
			velocity = CVector(0.0, 0.0, 0.0);

			Hat* hat = new Hat(position.x, position.z);
			hat->AttachTo(parentNode);
		}

		
		if(distFromPlayer < 200)
		{
			if(modelState == MODEL_RUN)//if models state is running
			{
				if((clock() - startShuffle) / CLOCKS_PER_SEC > 1)// 1 second has elapsed
				{
					if(distFromPlayer < 75) 
						audioSys->PlaySoundDX(audioSys->iSnowShuffle);

					else if(distFromPlayer > 75 && distFromPlayer < 200)
						audioSys->PlaySoundDX(audioSys->iSnowShuffle_low);

					//else, don't play sound
					startShuffle = clock();
				}
			}

			// if timer is three seconds passed
			if((clock() - start) / CLOCKS_PER_SEC > 3)
			{
				// pick random sound
				int r = rand() % 3;
				switch(r)
				{
				case 0:
					if(distFromPlayer < 75)
						audioSys->PlaySoundDX(audioSys->iZombieGroan1);
					else if(distFromPlayer > 75 && distFromPlayer < 200)
						audioSys->PlaySoundDX(audioSys->iZombieGroan1_low);
					break;
				case 1:
					if(distFromPlayer < 75)
						audioSys->PlaySoundDX(audioSys->iZombieGroan2);
					else if(distFromPlayer > 75 && distFromPlayer < 200)
						audioSys->PlaySoundDX(audioSys->iZombieGroan2_low);
					break;
				case 2:
					if(distFromPlayer < 75)
						audioSys->PlaySoundDX(audioSys->iZombieGroan3);
					else if(distFromPlayer > 75 && distFromPlayer < 200)
						audioSys->PlaySoundDX(audioSys->iZombieGroan3_low);
					break;
				default:
					audioSys->PlaySoundDX(audioSys->iGetToThaChoppa);
					break;
				}

				start = clock(); // reset timer
			}
		}
	}
}

